package com.fary.security.web.context.request.async;

import com.fary.security.core.context.SecurityContext;
import com.fary.security.core.context.SecurityContextHolder;
import com.fary.util.Assert;
import com.fary.web.context.request.NativeWebRequest;
import com.fary.web.context.request.async.CallableProcessingInterceptor;

import java.util.concurrent.Callable;

public final class SecurityContextCallableProcessingInterceptor implements CallableProcessingInterceptor {
    private volatile SecurityContext securityContext;

    /**
     * Create a new {@link SecurityContextCallableProcessingInterceptor} that uses the
     * {@link SecurityContext} from the {@link SecurityContextHolder} at the time
     * {@link #beforeConcurrentHandling(NativeWebRequest, Callable)} is invoked.
     */
    public SecurityContextCallableProcessingInterceptor() {
    }

    /**
     * Creates a new {@link SecurityContextCallableProcessingInterceptor} with the
     * specified {@link SecurityContext}.
     *
     * @param securityContext the {@link SecurityContext} to set on the
     *                        {@link SecurityContextHolder} in {@link #preProcess(NativeWebRequest, Callable)}.
     *                        Cannot be null.
     * @throws IllegalArgumentException if {@link SecurityContext} is null.
     */
    public SecurityContextCallableProcessingInterceptor(SecurityContext securityContext) {
        Assert.notNull(securityContext, "securityContext cannot be null");
        setSecurityContext(securityContext);
    }

    @Override
    public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) {
        if (securityContext == null) {
            setSecurityContext(SecurityContextHolder.getContext());
        }
    }

    @Override
    public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
        SecurityContextHolder.setContext(securityContext);
    }

    @Override
    public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
        SecurityContextHolder.clearContext();
    }

    private void setSecurityContext(SecurityContext securityContext) {
        this.securityContext = securityContext;
    }
}